#include "MagickImage.h"
#include "Color.h"
#include <vector>
#include <iterator>

using namespace std;
using boost::scoped_ptr;

// Constructs an empty image.
MagickImage::MagickImage() : image_() {}

// Copies the image.
MagickImage::MagickImage(const MagickImage& rhs) : image_() {
  image_.reset(new Magick::Image(*rhs.image_));
  // Magick::Image has underlying smart pointers.
  // modifyImage() ensures only one reference to an image by copying it.
  image_->modifyImage();
}

// Copies the image.
MagickImage& MagickImage::operator=(const MagickImage& rhs) {
  MagickImage temp(rhs);
  swap(temp);
  return *this;
}

// Swaps the image with another.
void MagickImage::swap(MagickImage& rhs) {
  image_.swap(rhs.image_);
}

// Loads an image from specified file.
int MagickImage::load(const string& filename) {
  scoped_ptr<Magick::Image> temp;

  try {
    temp.reset(new Magick::Image(filename));
  } catch (const Magick::Exception& ex) {
    return -1;
  }

  image_.swap(temp);
  return 0;
}

// Loads an image from raw data.
int MagickImage::loadFromStream(istream& stream) {
  // Do not skip whitespace.
  stream >> noskipws;
  // Beginning and end of stream.
  istream_iterator<unsigned char> begin(stream);
  istream_iterator<unsigned char> end;

  // Buffer to copy contents into.
  vector<unsigned char> buffer;
  copy(begin, end, back_inserter(buffer));

  Magick::Blob blob(&buffer[0], buffer.size());
  scoped_ptr<Magick::Image> temp;

  try {
    temp.reset(new Magick::Image(blob));
  } catch (const Magick::Exception& ex) {
    return -1;
  }

  image_.swap(temp);
  return 0;
}

// Saves an image. Format is determined by extension.
int MagickImage::save(const string& filename) const {
  try {
    image_->write(filename);
  } catch (const Magick::Exception& ex) {
    return -1;
  }

  return 0;
}

// Creates an empty image of given dimension with background color.
int MagickImage::create(int width, int height, const Color& color) {
  Magick::Geometry geometry(width, height);
  Magick::ColorRGB background(color.red(), color.green(), color.blue());
  scoped_ptr<Magick::Image> temp;

  try {
    temp.reset(new Magick::Image(geometry, background));
  } catch (const Magick::Exception& ex) {
    return -1;
  }

  image_.swap(temp);
  return 0;
}

// Returns the image width.
int MagickImage::width() const {
  return image_->columns();
}

// Returns the image height.
int MagickImage::height() const {
  return image_->rows();
}

// Returns the color of a pixel.
Color MagickImage::pixel(int i, int j) const {
  Magick::ColorRGB color(image_->pixelColor(j, i));
  return Color(color.red(), color.green(), color.blue());
}

// Sets the color of a pixel.
void MagickImage::setPixel(int i, int j, const Color& color) {
  Magick::ColorRGB magick_color(color.red(), color.green(), color.blue());
  image_->pixelColor(j, i, magick_color);
}

// Pastes another image over the current image. 
void MagickImage::composite(const MagickImage& image, int i, int j) {
  image_->composite(*image.image_, j, i, Magick::CopyCompositeOp);
}

